use crate::{runtime::FuncBinds, Env, Value};

mod array;
mod cmd;
mod cmp;
mod hash;
mod io;
mod math;
mod string;
mod r#type;

pub trait Builtin {
    fn call(&self, env: &Env, argv: &[Value]) -> Result<Value, ()>;
}

pub fn new_stdlib() -> FuncBinds {
    let mut env = FuncBinds::new();

    env.bind_builtin("glob", Glob);

    io::bind(&mut env);
    string::bind(&mut env);
    cmp::bind(&mut env);
    array::bind(&mut env);
    r#type::bind(&mut env);
    math::bind(&mut env);
    hash::bind(&mut env);
    cmd::bind(&mut env);

    env
}

// glob

struct Glob;

impl Builtin for Glob {
    fn call(&self, _: &Env, argv: &[Value]) -> Result<Value, ()> {
        let mut path_arr = Vec::new();

        for arg in argv {
            let path = match arg {
                Value::Symbol(path) => path,
                Value::String(path) => path,
                _ => {
                    eprintln!("glob: invalid argument {arg:?}");
                    return Err(());
                }
            };

            for each in glob::glob(path).map_err(|err| eprintln!("glob: invalid glob: {err}"))? {
                let each = each.map_err(|err| eprintln!("glob: get item: {err}"))?;
                path_arr.push(Value::Path(each));
            }
        }

        Ok(Value::Array(path_arr.into()))
    }
}
